<?php

// +----------------------------------------------------------------------
// | 结算、回款 服务层
// +----------------------------------------------------------------------
// | 主要服务于结算、回款
// +----------------------------------------------------------------------
// | 错误编码头 240xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\exception\LogicException;
use common\model\ContractModel;
use common\model\CustomerInvoiceApplyModel;
use common\model\InvoiceModel;
use common\model\MediaModel;
use common\model\OrderModel;
use common\model\ReviewRequestNodeModel;
use common\model\SettlementModel;
use common\model\UserModel;
use support\bootstrap\Log;
use support\ErrorCode;
use think\Exception;
use think\module\Module;
use Throwable;
use Webman\Stomp\Client;

class FinanceService
{

    /**
     * 查询回款数据
     * @param $param
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function selectReceivableData($param, $tenantId)
    {
        $cModuleCode = "settlement";
        $settlementModel = model(SettlementModel::class);
        $param['filter'] = append_filter_param($param, $cModuleCode, [
            'is_reviewed' => 'yes',
        ]);
        $param['filter'] = append_filter_param($param, "order", [
            'to_tenant_id' => $tenantId,
        ]);
        $resData = $settlementModel->selectData($param);
        if (!empty($resData['rows'])) {
            $userIdArray = [];
            $fromTenantIdArray = [];
            $settlementIdArray = [];
            foreach ($resData['rows'] as &$row) {
                if ($row['settlement'] && $row['settlement']['created_by']) {
                    $userIdArray[$row['settlement']['created_by']] = $row['settlement']['created_by'];
                }
                if ($row['settlement'] && $row['settlement']['person_in_charge']) {
                    $userIdArray[$row['settlement']['person_in_charge']] = $row['settlement']['person_in_charge'];
                }
                if ($row['order'] && $row['order']['from_tenant_id']) {
                    $fromTenantIdArray[$row['order']['from_tenant_id']] = $row['order']['from_tenant_id'];
                }
                if ($row['settlement'] && $row['settlement']['id']) {
                    $settlementIdArray[$row['settlement']['id']] = $row['settlement']['id'];
                }
            }

            //发票信息
            $invoiceMap = [];
            $invoiceModel = model(InvoiceModel::class);
            if (!empty($settlementIdArray)) {
                $invoiceData = $invoiceModel
                    ->where(['receivable_id' => ['IN', implode(',', $settlementIdArray)]])
                    ->join("media ON media.id = invoice.attached_id")
                    ->field("invoice.*,media.thumb,media.param")
                    ->distinct(true)->select();
                $invoiceData = array_column($invoiceData, null, 'receivable_id');
                $invoiceMap = array_map(function ($item) {
                    if (!empty($item['param'])) {
                        $item['param'] = json_decode($item['param'], true);
                    }
                    return $item;
                }, $invoiceData);

            }
            //创建人
            $userMap = [];
            if (!empty($userIdArray)) {
                $userService = UserService::getInstance();
                $userList = $userService->selectLocalUserList(['id' => ["IN", implode(',', $userIdArray)]], ["id", "name", "email", "avatar"]);
                $userMap = array_column($userList, null, 'id');
            }

            //供应商信息
            $supplierMap = [];
            if (!empty($fromTenantIdArray)) {
                $requestData = ['param' => [
                    "filter" => [
                        'tenant_id' => ["-in", implode(',', $fromTenantIdArray)],
                    ],
                    "fields" => "id,company_name,tenant_id"
                ]];
                $supplierData = teamones_request('teamones-im', 'tenant_authentication/select', $requestData)['data'];
                $supplierMap = array_column($supplierData['rows'], null, 'tenant_id');

            }


            $resData['rows'] = array_map(function ($item) use (&$invoiceMap, &$userMap, &$supplierMap) {
                $item['settlement']['receivable'] = $invoiceMap[$item['settlement']['id']] ?? null;
                $item['settlement']['created_by'] = $userMap[$item['settlement']['created_by']] ?? null;
                $item['settlement']['person_in_charge'] = $userMap[$item['settlement']['person_in_charge']] ?? null;
                $item['order']['from_tenant'] = $supplierMap[$item['order']['from_tenant_id']] ?? null;
//                $item['settlement']['payment_time'] = get_format_date($item['settlement']['payment_time'], 1);
                return $item;
            }, $resData['rows']);
        }
        return $resData;
    }


    /**
     * 查询结算数据
     * @param $param
     * @param int $tenantId
     * @param $cModuleCode
     * @return mixed
     * @throws \Exception
     */
    public function selectSettlementData($param, int $tenantId, $cModuleCode)
    {

        $settlementModel = model(SettlementModel::class);
        $param['filter'] = append_filter_param($param, $cModuleCode, [
            'is_reviewed' => 'yes',
        ]);

        // 平台方确认结算的发票信息 进行打款


        $tenantService = TenantService::getInstance();
        $platformId = $tenantService->getPlatformTenantId();
        $isPlatform = $tenantId == $platformId;
        if (!$isPlatform) {
            $param['filter'] = append_filter_param($param, "order", [
                'from_tenant_id' => $tenantId,
            ]);
        }


        $resData = $settlementModel->selectData($param);

        if (!empty($resData['rows'])) {
            $userIdArray = [];
            $toTenantIdArray = [];
            $settlementIdArray = [];
            foreach ($resData['rows'] as $row) {
                if ($row['settlement'] && $row['settlement']['created_by']) {
                    $userIdArray[$row['settlement']['created_by']] = $row['settlement']['created_by'];
                }
                if ($row['settlement'] && $row['settlement']['person_in_charge']) {
                    $userIdArray[$row['settlement']['person_in_charge']] = $row['settlement']['person_in_charge'];
                }
                if ($row['order'] && $row['order']['to_tenant_id']) {
                    $toTenantIdArray[$row['order']['to_tenant_id']] = $row['order']['to_tenant_id'];
                }

                if ($row['settlement'] && $row['settlement']['id']) {
                    $settlementIdArray[$row['settlement']['id']] = $row['settlement']['id'];
                }
            }

            //发票信息
            $invoiceModel = model(InvoiceModel::class);
            if (!empty($settlementIdArray)) {
                $invoiceData = $invoiceModel
                    ->where(['receivable_id' => ['IN', implode(',', $settlementIdArray)]])
                    ->join("media ON media.id = invoice.attached_id")
                    ->field("invoice.*,media.thumb,media.param")
                    ->distinct(true)->select();
                $invoiceData = array_column($invoiceData, null, 'receivable_id');

                $resData['rows'] = array_map(function ($item) use ($invoiceData) {
                    $item['settlement']['receivable'] = $invoiceData[$item['settlement']['id']] ?? null;
                    return $item;
                }, $resData['rows']);

            }

            //创建人
            if (!empty($userIdArray)) {
                $userService = UserService::getInstance();
                $userList = $userService->selectLocalUserList(['id' => ["IN", implode(',', $userIdArray)]], ["id", "name", "email", "avatar"]);
                $userList = array_column($userList, null, 'id');
                $resData['rows'] = array_map(function ($item) use ($userList) {
                    $item['settlement']['created_by'] = $userList[$item['settlement']['created_by']] ?? null;
                    $item['settlement']['person_in_charge'] = $userList[$item['settlement']['person_in_charge']] ?? null;
                    return $item;
                }, $resData['rows']);
            }

            //供应商信息
            if (!empty($toTenantIdArray)) {
                $requestData = ['param' => [
                    "filter" => [
                        'tenant_id' => ["-in", implode(',', $toTenantIdArray)],
                    ],
                    "fields" => "id,company_name,tenant_id"
                ]];
                $supplierData = teamones_request('teamones-im', 'tenant_authentication/select', $requestData)['data'];
                $supplierData = array_column($supplierData['rows'], null, 'tenant_id');
                $resData['rows'] = array_map(function ($item) use ($supplierData) {
                    $item['order']['to_tenant'] = $supplierData[$item['order']['to_tenant_id']] ?? null;
                    return $item;
                }, $resData['rows']);
            }
        }
        $resData['is_platform'] = $isPlatform;
        return $resData;
    }

    /**
     * 添加结算 并发起结算审批申请
     * @param $data
     * @return mixed
     * @throws Exception
     * @throws Throwable
     */
    public function addSettlement($data)
    {
        $orderModel = model(OrderModel::class);

        $fields = [
            'order.id',
            'order.name',
            'order.number',
            'order.man_day',
            'order.man_day_number',
            'order.money',
            'order.from_tenant_id',
            'order.to_tenant_id',
            'order.project_id',
            'contract.id',
            'contract.status',
            'contract.order_id',
            'contract.number',
            'contract.money',
        ];

        $fields = build_complex_query_fields($fields, '__');

        $orderData = $orderModel
            ->join("contract ON contract.order_id = order.id")
            ->where([
                'order.id' => $data['order_id'],
                'contract.status' => 'signed',
                'contract.type' => 'contract_type_order',
            ])
            ->field($fields)
            ->find();
        $orderData = separate_complex_query_data($orderData, '__');

        $settlementModel = model(SettlementModel::class);
        $data['number'] = date("YmdH") . random_int(11111, 99999);

        if (empty($orderData['order'])) {
            throw new LogicException("missing order", ErrorCode::MISSING_ORDER);
        }
        if (empty($orderData['contract'])) {
            throw new LogicException("missing order contract", ErrorCode::MISSING_ORDER_CONTRACT);
        }

        $data['contract_id'] = $orderData['contract']['id'];
        $data['project_id'] = $orderData['order']['project_id'];
        $settlementModel->startTrans();

        try {
            $flag = $settlementModel->addItem($data);
            if (!$flag) {
                throw new LogicException("add settlement record failure" . $settlementModel->getError(), ErrorCode::SETTLEMENT_RECORD_ADD_FAILURE);
            }
            $reviewRequest = $this->addReviewRecord($flag);
            // 修改任务状态为 待结算审核
            $orderChangeFlag = $orderModel->save([
                'id' => $data['order_id'],
                'status' => 'wait_for_review_settlement',
            ]);

            if (!$orderChangeFlag) {
                throw new LogicException("update order status failure" . $orderModel->getError(), ErrorCode::ORDER_STATUS_UPDATE_FAILURE);
            }
            $settlementModel->commit();
            return array_merge($flag, $reviewRequest);
        } catch (Throwable $e) {
            $settlementModel->rollback();
            throw $e;
        }
    }


    /**
     * 添加结算审批数据
     * @param $settlementData
     * @return mixed
     * @throws Exception
     * @throws Throwable
     */
    private function addReviewRecord($settlementData)
    {
        $reviewWorkflowId = $this->getSettlementReviewConfig();
        $financeReviewWorkflowService = FinanceReviewWorkflowService::getInstance();
        $reviewConfig = $financeReviewWorkflowService->selectReview($reviewWorkflowId);
        if (empty($reviewConfig)) {
            throw new LogicException("missing order settlement review config", ErrorCode::MISSING_ORDER_SETTLEMENT_REVIEW_CONFIG);
        }

        //查询订单信息
        $order = model(OrderModel::class)->where(['id' => $settlementData['order_id']])->field("party_a_person_in_charge")->find();
        if (empty($order)) {
            throw new LogicException("missing order data", ErrorCode::MISSING_ORDER_DATA);
        }
        // 取出订单 甲方负责人
        $personAInCharge = $order['party_a_person_in_charge'];

        $reviewers = [];
        foreach ($reviewConfig['matched_data']['reviewers'] as $reviewer) {

            if (count($reviewer['user_info']) == 1) {
                $reviewUserId = $reviewer['user_info'][0]['id'];
            } else {
                $userIds = array_column($reviewer['user_info'], 'id');
                if (in_array($personAInCharge, $userIds)) {
                    // 如果甲方负责人在当前node的审核人里 那么直接选中负责人为审核人
                    $reviewUserId = $personAInCharge;
                } else {
                    // 如果不在 就在审核人列表里找一个 部位当前发起人的用户
                    foreach ($reviewer['user_info'] as $reviewerUser) {
                        if ($reviewerUser['id'] != request()->getUserId()) {
                            $reviewUserId = $reviewerUser['id'];
                        }
                    }
                }
            }

            $reviewers[] = [
                'role_code' => $reviewer['role_code'],
                'user_id' => $reviewUserId,
//                'skip' => $reviewUserId == request()->getUserId(),
                'skip' => 0,
            ];
        }

        $reviewRequestService = ReviewRequestService::getInstance();

        $settlementModule = Module::$moduleDictData['module_index_by_code']['settlement'];
        $reviewData = [
            'reviewers' => $reviewers,
            'data' => [
                'review_workflow_id' => $reviewConfig['review_workflow']['id'],
                'name' => "结算{$settlementData['number']}",
                'param' => $settlementData,
                'link_id' => $settlementData['id'],
                'module_id' => $settlementModule['id'],
            ],
        ];
        $res = $reviewRequestService->create($reviewData);
        APP_DEBUG && trace("add settlement review request {$reviewData['data']['name']}", "DEBUG");
        if (count($res['nodes']) > 0) {
            try {
                // 通知审核人 进行结算审批
                $settlement = model(SettlementModel::class)->find($res['review_request']['link_id']);
                $this->notifyReviewSettlement($settlement, $res['nodes'][0]);
            } catch (Throwable $e) {
                trace($e->getMessage() . PHP_EOL . $e->getTraceAsString(), "ERR");
            }
        }
        return $res;
    }

    /**
     * 获得结算审核流配置值
     * @return int
     */
    private function getSettlementReviewConfig()
    {
        return (int)OptionsService::getInstance()->getOptionsConfigItemData('order_review_config', 'settlement_created_review');
    }

    /**
     * 处理结算审核状态更新
     * @param $settlementId
     * @param bool $isPass
     * @throws Exception
     * @throws Throwable
     */
    public function handleSettlementReviewed($settlementId, bool $isPass)
    {

        $settlementModel = model(SettlementModel::class);
        $orderModel = model(OrderModel::class);
        $settlement = $settlementModel->where(['id' => $settlementId])->find();
        $order = $orderModel->where(['id' => $settlement['order_id']])->find();
        if (empty($settlement) || empty($order)) {
            throw new LogicException("missing settlement data", ErrorCode::MISSING_SETTLEMENT_DATA);
        }
        if (empty($order)) {
            throw new LogicException("missing order data", ErrorCode::MISSING_ORDER_DATA);
        }

        if ($isPass) {
            //  审核通过  订单进入结算待打款状态
            $orderChangeStatus = 'wait_for_settlement';
            $settlementChangeStatus = 'yes';
        } else {
            // 审核不通过 订单进入结算待提交审核状态
            $orderChangeStatus = 'wait_for_add_settlement';
            $settlementChangeStatus = 'no';
        }

        $orderModel->startTrans();
        try {
            $settlementModel->modifyItem([
                'id' => $settlement['id'],
                'is_reviewed' => $settlementChangeStatus,
            ]);

            $orderModel->modifyItem([
                'id' => $order['id'],
                'status' => $orderChangeStatus,
            ]);
            $orderModel->commit();
        } catch (Throwable $e) {
            $orderModel->rollback();
            throw $e;
        }
    }

    /**
     * 上传发票
     * @param $data
     * @param $tenantId
     * @return array|bool
     * @throws Throwable
     */
    public function uploadInvoice($data, $tenantId)
    {
        $mediaService = MediaService::getInstance();
        if (!empty($data['attached_media'])) {
            $mediaItem = $mediaService->generateMediaData($data['attached_media']);
        }

        $invoiceModel = model(InvoiceModel::class);
        $mediaModel = model(MediaModel::class);

        $orderModel = model(OrderModel::class);

        $order = $orderModel->find($data['order_id']);
        if (empty($order)) {
            throw new LogicException("missing Order", ErrorCode::MISSING_ORDER);
        }

        $orderInvoiceStatusField = "";
        if ($tenantId == $order['from_tenant_id']) {
            $orderInvoiceStatusField = "party_a_invoice_status";
        } else if ($tenantId == $order['to_tenant_id']) {
            $orderInvoiceStatusField = "party_b_invoice_status";
        } else {
            throw new LogicException("request data error", ErrorCode::REQUEST_DATA_ERROR);
        }

        $invoiceModel->startTrans();
        try {
            $mediaData = null;
            if (!empty($mediaItem)) {
                $mediaData = $mediaModel->addItem($mediaItem);
                if (!$mediaData) {
                    throw new LogicException("add media data failure" . $mediaModel->getError(), ErrorCode::MEDIA_ADD_FAILURE);
                }
                $data['attached_id'] = $mediaData['id'];
            }

            $data['tenant_id'] = $tenantId;
            $res = $invoiceModel->addItem($data);
            if (!$res) {
                throw new LogicException("save invoice data failure" . $invoiceModel->getError(), ErrorCode::INVOICE_SAVE_FAILURE);
            }

            // 修改订单发票上传状态
            $orderModel->modifyItem([
                'id' => $order['id'],
                $orderInvoiceStatusField => "uploaded"
            ]);
            $invoiceModel->commit();
            $res['attached_media'] = $mediaData;
        } catch (Throwable $e) {
            $invoiceModel->rollback();
            throw $e;
        }

        // 提示对方确认发票
        try {
            $this->notifyPartyAToEnsureInvoice($res['id']);
        } catch (Throwable $e) {
            Log::error("##########################");
            Log::error($e->getMessage());
            Log::error($e->getCode());
            Log::error($e->getTraceAsString());
            Log::error("##########################");
        }
        return $res;
    }

    /**
     * 确认发票
     * @param $param
     * @param $userId
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function ensureInvoice($param, $userId)
    {
        $invoiceModel = model(InvoiceModel::class);
        $orderModel = model(OrderModel::class);
        $settlementModel = model(SettlementModel::class);

        $invoice = $invoiceModel->where($param['filter'])->find();
        if (empty($invoice)) {
            throw new LogicException("can`t found match invoice record", ErrorCode::MATCH_INVOICE_RECORD_NOT_FOUND);
        }
        $order = $orderModel->find($invoice['order_id']);
        if (empty($order)) {
            throw new LogicException("can`t found match order record", ErrorCode::MATCH_ORDER_RECORD_NOT_FOUND);
        }
        $data = [
            'id' => $invoice['id'],
            'confirm_receipt' => 'yes',
            'confirm_user_id' => $userId
        ];
        $invoiceModel->startTrans();
        try {
            $flag = $invoiceModel->modifyItem($data);
            if (!$flag) {
                throw new LogicException($invoiceModel->getError(), ErrorCode::INVOICE_MODIFY_FAILURE);
            }

            // 修改结算得打款时间
            $settlementFlag = $settlementModel->save([
                'id' => $flag['receivable_id'],
                'payment_time' => time(),
            ]);
            if (!$settlementFlag) {
                throw new LogicException($settlementModel->getError(), ErrorCode::PAYMENT_TIME_FOR_SETTLEMENT_MODIFY_FAILURE);
            }
            $orderModel->modifyItem([
                'id' => $order['id'],
                'status' => 'wait_for_settlement',
            ]);

            //  平台打款 给乙方
            $requestData = [
                "id" => $order['id'],
                'tenant_id' => $order['to_tenant_id'],
                'belong_system' => C("belong_system"),
                'money' => $order['money'],
            ];
            teamones_request('teamones-im', 'user/order_increase', $requestData);
            $invoiceModel->commit();

        } catch (Throwable $e) {
            $invoiceModel->rollback();
            throw $e;
        }

        // 甲方确认发票 通知乙方
        try {
            $this->notifyPartyBInvoiceConfirmed($invoice['id']);
        } catch (Throwable $e) {
            trace($e->getMessage() . PHP_EOL . $e->getTraceAsString(), 'ERR');
        }
        return $flag;
    }

    /**
     * 乙方确认收款
     * @param $param
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function ensureSettlement($param)
    {
        $settlementModel = model(SettlementModel::class);
        $orderModel = model(OrderModel::class);

        $ensureSettlement = $settlementModel->findData($param);
        if (empty($ensureSettlement)) {
            throw new LogicException("can`t found match record", ErrorCode::MATCH_RECORD_NOT_FOUND);
        }
        $settlementModel->startTrans();
        try {
            $flag = $settlementModel->save([
                'id' => $ensureSettlement['id'],
                'payment_time' => time(),
                'is_complete' => "yes",
            ]);
            if (!$flag) {
                throw new LogicException("update settlement status failure " . $settlementModel->getError(), ErrorCode::SETTLEMENT_STATUS_UPDATE_FAILURE);
            }
            $flag = $orderModel->modifyItem([
                'id' => $ensureSettlement['order_id'],
                'status' => "done",
            ]);
            if (!$flag) {
                throw new LogicException("update order status failure " . $orderModel->getError(), ErrorCode::ORDER_STATUS_UPDATE_FAILURE);
            }
            $settlementModel->commit();
        } catch (Throwable $e) {
            $settlementModel->rollback();
            throw $e;
        }
        //通知甲方已确认
        try {
            $this->notifyPartyASettlementConfirmed($flag['id']);
        } catch (Throwable $e) {
            trace($e->getMessage() . PHP_EOL . $e->getTraceAsString(), 'ERR');
        }

        //关闭订单和结算群聊
        try {
            $this->closeOrderSettlementChat($ensureSettlement['id']);
        } catch (Throwable $e) {
            Log::error("##########################");
            Log::error($e->getMessage());
            Log::error($e->getCode());
            Log::error($e->getTraceAsString());
            Log::error("##########################");
        }

        return $settlementModel->where(['id' => $ensureSettlement['id']])->findData();
    }

    /**
     * 通知审核人 进行结算审批
     * todo 待实现
     * @param $settlement
     * @param $node
     * @throws \Exception
     */
    public function notifyReviewSettlement($settlement, $node)
    {
        $order = model(OrderModel::class)->find($settlement['order_id']);

        $userIdData = [
            $settlement['created_by'], $node['plan_reviewer']
        ];

        $userMap = model(UserModel::class)->where(['id' => ['IN', implode(',', $userIdData)]])->select();

        $messageParam = [
            'first' => '任务已通过审核',
            'keyword1' => $userMap[$settlement['created_by']]['name'],
            'keyword2' => $order['name'],
            'keyword3' => date("Y/m/d H:i"),
            'remark' => '请及时审批'
        ];
        $messageTemplateId = $this->getNotifyReviewSettlementTemplateId();
        $actions = [
            [
                'key' => "mark_read",
                'text' => '标记已读',
                'param' => null
            ],
        ];
        $jobParams = [
            "param" => $messageParam,
            'receiver' => $userMap[$node['plan_reviewer']]['union_id'],
            'template_id' => $messageTemplateId,
            'content_type' => 'card_message',
            'actions' => $actions,
            'notice_type' => 'message_order',
            'message_key' => 'review_settlement_notice',
        ];
        Client::send('send_notify_message', build_queue_data_with_xu($jobParams));

    }

    /**
     * 获得订单发起结算后 结算审批提醒模板id
     * @return int
     */
    private function getNotifyReviewSettlementTemplateId()
    {
        $templateId = OptionsService::getInstance()->getOptionsConfigItemData('', '');
        return (int)$templateId;
    }

    /**
     * 结算审批结束后 通知甲乙双方结果
     * @param array $reviewRequest
     * @param bool $isPass
     * @throws \Exception
     */
    public function notifyReviewSettlementResult(array $reviewRequest, bool $isPass)
    {
        $settlementId = $reviewRequest['link_id'];
        $settlement = model(SettlementModel::class)->find($settlementId);
        $partyATemplateId = $this->getNotifyReviewSettlementResultPartyATemplateId();
        $order = model(OrderModel::class)->find($settlement['order_id']);
        $node = model(ReviewRequestNodeModel::class)
            ->where([
                'review_request_id' => $reviewRequest['id'],
                'is_reviewed' => "yes",
            ])->order("id DESC")->find();
        $settlementCreator = model(UserModel::class)->find($settlement['created_by']);
        $reviewer = model(UserModel::class)->find($node['plan_reviewer']);
        $contract = model(ContractModel::class)->where(['order_id' => $order['id']])->find();

        $notifyMessages = [];
        if ($isPass) {
            $actions = [
                [
                    'key' => "purchase_order",
                    'text' => '查看详情',
                    'param' => null
                ],
            ];
        } else {
            $actions = [
                [
                    'key' => "order_add_settlement",
                    'text' => '查看详情',
                    'param' => [
                        'order_id' => $order['id'],
                        'contract_id' => $contract['id']
                    ]
                ],
            ];
        }
        $notifyMessages[] = [
            'param' => [
                'first' => '结算审批已完成',
                'order_name' => $order['name'],
                'review_user_name' => $reviewer['name'],
                'review_at' => date("Y/m/d H:i"),
                'review_result' => $isPass ? "通过" : "未通过",
                'remark' => $isPass ? "已生成结算" : '请重新提交结算审批'
            ],
            'template_id' => $partyATemplateId,
            'receiver_id' => $settlementCreator['union_id']
        ];

        if ($isPass) {
            $partyBNotifyUser = model(UserModel::class)->find($contract['party_b_signer']);
            $requestData = ['param' => ['filter' => [
                'id' => $contract['from_tenant_id']
            ]]];
            $partyBTenantData = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];
            $templateId = $this->getNotifyReviewSettlementResultPartyBTemplateId();
            $notifyMessages[] = [
                'param' => [
                    'first' => '合同已经开始结算',
                    'contract_name' => $contract['name'],
                    'customer_name' => $partyBTenantData['name'],
                    'remark' => '请及时开票'
                ],
                'template_id' => $templateId,
                'receiver_id' => $partyBNotifyUser['union_id']
            ];

        }
        foreach ($notifyMessages as $notifyMessage) {
            Client::send('send_notify_message_v2', build_queue_data_with_xu([
                "param" => $notifyMessage['param'],
                'receiver' => $notifyMessage['receiver_id'],
                'template_id' => $notifyMessage['template_id'],
                'content_type' => 'card_message',
                'notice_type' => 'message_order',
                'message_key' => 'review_settlement_result',
            ]));
        }
    }

    /**
     * 甲方结算审核通过后 通知甲方模板id
     * @return int
     */
    private function getNotifyReviewSettlementResultPartyATemplateId()
    {
        $templateId = OptionsService::getInstance()->getOptionsConfigItemData("message_notify_template_config", "notify_review_settlement_result_party_a_template_id");
        return (int)$templateId;
    }

    /**
     * 甲方结算审核通过后 通知乙方签署人模板id
     * @return int
     */
    private function getNotifyReviewSettlementResultPartyBTemplateId()
    {
        $templateId = OptionsService::getInstance()->getOptionsConfigItemData("message_notify_template_config", "notify_review_settlement_result_party_b_template_id");
        return (int)$templateId;
    }

    /**
     * 提示甲方签约人发票确认
     * @param $invoiceId
     * @throws \Exception
     */
    private function notifyPartyAToEnsureInvoice($invoiceId)
    {
        $invoice = model(InvoiceModel::class)->find($invoiceId);
        $contract = model(ContractModel::class)->find($invoice['contract_id']);
        $requestData = ['param' => ['filter' => [
            'id' => $contract['to_tenant_id']
        ]]];
        $tenantData = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];

        $messageParam = [
            'first' => '发票已开',
            'contract_name' => $contract['name'],
            'supplier_name' => $tenantData['name'],
            'invoicing_at' => date("Y/m/d H:i"),
            'settlement_id' => $invoice['receivable_id'],
            'remark' => '请注意查收',
        ];
        $messageTemplateId = $this->getNotifyPartyAToEnsureInvoiceTemplateId();

        $notifyUser = model(UserModel::class)->find($contract['party_a_signer']);
        Client::send('send_notify_message_v2', build_queue_data_with_xu([
            "param" => $messageParam,
            'receiver' => $notifyUser['union_id'],
            'template_id' => $messageTemplateId,
            'content_type' => 'card_message',
            'notice_type' => 'message_order',
            'message_key' => 'party_a_ensure_invoice',
        ]));
    }


    /**
     * 获得开票通知模板 id
     * @return int
     */
    private function getNotifyPartyAToEnsureInvoiceTemplateId()
    {
        $optionsService = OptionsService::getInstance();
        $templateId = $optionsService->getOptionsConfigItemData("message_notify_template_config", "notify_party_a_to_ensure_invoice_template_id");
        return (int)$templateId;
    }

    /**
     * 乙方确认收款 通知甲方签约人
     * @param int $settlementId
     * @throws \Exception
     */
    private function notifyPartyASettlementConfirmed(int $settlementId)
    {
        $settlement = model(SettlementModel::class)->find($settlementId);
        $contract = model(ContractModel::class)->find($settlement['contract_id']);
        $requestData = ['param' => ['filter' => [
            'id' => $contract['to_tenant_id']
        ]]];
        $tenantData = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];
        $messageParam = [
            'first' => '财务人员已完成结算',
            'contract_name' => $contract['name'],
            'supplier_name' => $tenantData['name'],
            'done_at' => date("Y/m/d H:i"),
            'remark' => '请及时处理',
        ];
        $messageTemplateId = $this->getNotifyPartyASettlementConfirmedTemplateId();
        $notifyUser = model(UserModel::class)->field('union_id')->find($contract['party_a_signer']);
        Client::send('send_notify_message_v2', build_queue_data_with_xu([
            "param" => $messageParam,
            'receiver' => $notifyUser['union_id'],
            'template_id' => $messageTemplateId,
            'content_type' => 'card_message',
            'notice_type' => 'message_order',
            'message_key' => 'party_a_settlement_confirmed',
        ]));
    }

    /**
     * 乙方确认收款 通知甲方模板id
     * @return int
     */
    private function getNotifyPartyASettlementConfirmedTemplateId()
    {
        $optionsService = OptionsService::getInstance();
        $templateId = $optionsService->getOptionsConfigItemData("message_notify_template_config", "notify_party_a_settlement_confirmed_template_id");
        return (int)$templateId;
    }

    /**
     * 甲方确认发票 通知乙方
     * @param int $invoiceId
     * @throws \Exception
     */
    private function notifyPartyBInvoiceConfirmed(int $invoiceId)
    {
        $invoice = model(InvoiceModel::class)->find($invoiceId);
        $contract = model(ContractModel::class)->find($invoice['contract_id']);
        $requestData = ['param' => ['filter' => [
            'id' => $contract['from_tenant_id']
        ]]];
        $tenantData = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];

        $messageParam = [
            'first' => '对方已确认收到发票',
            'contract_name' => $contract['name'],
            'supplier_name' => $tenantData['name'],
            'done_at' => date("Y/m/d H:i"),
            'remark' => '请注意收款',
        ];
        $messageTemplateId = $this->getNotifyPartyBInvoiceConfirmedTemplateId();
        $notifyUser = model(UserModel::class)->field('union_id')->find($contract['party_b_signer']);
        Client::send('send_notify_message_v2', build_queue_data_with_xu([
            "param" => $messageParam,
            'receiver' => $notifyUser['union_id'],
            'template_id' => $messageTemplateId,
            'content_type' => 'card_message',
            'notice_type' => 'message_order',
            'message_key' => 'party_b_invoice_confirmed',
        ]));
    }

    /**
     * 甲方确认发票 通知乙方消息模板id
     * @return int
     */
    private function getNotifyPartyBInvoiceConfirmedTemplateId()
    {
        $optionsService = OptionsService::getInstance();
        $templateId = $optionsService->getOptionsConfigItemData("message_notify_template_config", "notify_party_b_invoice_confirmed_template_id");
        return (int)$templateId;
    }

    /**
     * 创建订单结算会话
     * @param $settlementId
     * @throws \Exception
     */
    public function createOrderFinanceChat($settlementId)
    {
        $settlement = model(SettlementModel::class)->find($settlementId);
        if (empty($settlement)) {
            throw new LogicException("can`t found settlement {$settlementId}", ErrorCode::SETTLEMENT_NOT_FOUND);
        }
        $contract = model(ContractModel::class)->find($settlement['contract_id']);
        if (empty($contract)) {
            throw new LogicException("can`t found contract {$settlement['contract_id']}", ErrorCode::CONTRACT_NOT_FOUND);
        }
        $order = model(OrderModel::class)->find($settlement['order_id']);
        if (empty($order)) {
            throw new LogicException("can`t found order {$settlement['order_id']}", ErrorCode::ORDER_NOT_FOUND);
        }

        $chatUsers = [
            $contract['party_a_signer'],
            $contract['party_b_signer'],
        ];

        $users = model(UserModel::class)->where(['id' => ['IN', implode(',', $chatUsers)]])->field("union_id")->select();
        $chatUserUnionIds = array_unique(array_column($users, 'union_id'));
        $chatUserUnionIds = array_filter($chatUserUnionIds, function ($item) {
            return (int)$item > 0;
        });
        $chatUserUnionIds = empty($chatUserUnionIds) ? [] : $chatUserUnionIds;
        $requestData = ['param' => ['filter' => [
            'link_id' => $settlementId,
            'link_type' => 'settlement',
        ]]];
        $chat = teamones_request('teamones-im', 'oauth/find_chat', $requestData)['data'];
        if (empty($chat)) {
            $requestData = ['data' => [
                'chat' => [
                    'name' => $order['name'],
                    'type' => 'group',
                    'link_id' => $settlementId,
                    'link_type' => 'settlement',
                    'from_tenant_id' => $order['from_tenant_id'],
                    'to_tenant_id' => $order['to_tenant_id'],
                ],
                'chat_users' => $chatUserUnionIds,
                'owner' => 0
            ]];
            teamones_request('teamones-im', 'oauth/create_chat', $requestData);
            APP_DEBUG && trace("createChat chat {$order['name']}  ,chatUsers is " . var_export($chatUserUnionIds, true), 'DEBUG');
            APP_DEBUG && trace(__METHOD__ . "PROCESS DONE", "DEBUG");
            return;
        }

        if (count($chatUserUnionIds) > 0) {
            $requestData = ['data' => [
                'chat_global_id' => $chat['global_id'],
                'user_id' => $chatUserUnionIds,
            ]];
            teamones_request('teamones-im', 'oauth/invite_chat_user', $requestData);
            APP_DEBUG && trace("inviteChatUser to chat {$chat['global_id']} " . var_export($chatUserUnionIds, true), 'DEBUG');
        }
        APP_DEBUG && trace(__METHOD__ . "PROCESS DONE", "DEBUG");
    }

    /**
     *  解散订单和结算群聊
     * @param int $settlementId
     * @throws \Exception
     */
    private function closeOrderSettlementChat(int $settlementId)
    {

        $settlement = model(SettlementModel::class)->find($settlementId);
        if (empty($settlement)) {
            throw new LogicException("can`t found settlement {$settlementId}", ErrorCode::SETTLEMENT_NOT_FOUND);
        }
        $requestData = ['param' => ['filter' => [
            [
                'link_id' => $settlement['id'],
                'link_type' => 'settlement'
            ],
            [
                'link_id' => $settlement['order_id'],
                'link_type' => 'order'
            ],
            '_logic' => 'or'
        ]]];
        teamones_request('teamones-im', 'oauth/delete_chat', $requestData);
    }

    /**
     * 获得开票申请列表
     * @param $param
     * @param $currentTenantId
     * @return array
     * @throws \Exception
     */
    public function getCustomerInvoiceApplyList($param, $currentTenantId)
    {
        // 获得开票申请列表

        $applyModel = model(CustomerInvoiceApplyModel::class);
        $tenantService = TenantService::getInstance();
        $platformTenantId = $tenantService->getPlatformTenantId();

        $filter = $param['filter'] ?? [];
        $order = $param['order'] ?? "id DESC";
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];

        if ($platformTenantId != $currentTenantId) {
            $filter['tenant_id'] = $currentTenantId;
        }


        $applyFields = [
            "id",
            "link_ids",
            "attach_id",
            "money",
            "invoice_number",
            "status",
            "tenant_id",
            "created",
            "updated",
            "ship_number",
            "invoice_type",
        ];

        $queryFields = [
            "order.id",
            "order.name",
            "order.number",
            "order.money",
            "order.created",
            "order.created_by",
            "settlement.id",
            "settlement.number",
            "settlement.money",
            "settlement.remark",
            "settlement.payment_time",
            "settlement.is_reviewed",
        ];

        $fields = build_complex_query_fields($queryFields, '__');
        $total = $applyModel
            ->where($param['filter'])->count();
        $rows = $applyModel
            ->where($param['filter'])
            ->field(implode(',', $applyFields))
            ->order($order)
            ->page($page[0], $page[1])
            ->select();

        $orderIdMap = [];
        foreach ($rows as &$row) {
            $tmpArray = explode(',', $row['link_ids']);
            $row['order_id'] = [];
            if (count($tmpArray) > 0) {
                $row['order_id'] = $tmpArray;
                foreach ($tmpArray as $orderId) {
                    $orderIdMap[$orderId] = $orderId;
                }
            }
        }
        $orderMap = [];
        if (count($orderIdMap) > 0) {
            $orderList = model(OrderModel::class)->join("settlement ON settlement.order_id = `order`.id")
                ->where(["`order`.id" => ["IN", implode(',', $orderIdMap)]])
                ->field(implode(',', $fields))
                ->select();
            $orderMap = array_column($orderList, null, 'order__id');
        }


        $mediaIdArray = array_column($rows, 'attach_id', 'attach_id');
        $tenantIdArray = array_column($rows, 'tenant_id', 'tenant_id');

        $mediaMap = [];
        if (count($mediaIdArray) > 0) {
            $medias = model(MediaModel::class)
                ->where(['id' => ['IN', implode(',', $mediaIdArray)]])
                ->select();
            $mediaMap = array_column($medias, null, 'id');
        }
        $tenantAuthenticationMap = [];
        if (count($tenantIdArray) > 0) {
            $requestData = ['param' => [
                'filter' => [
                    "tenant_id" => ['-in', implode(',', $tenantIdArray)]
                ],
                'page' => [1, count($tenantIdArray)],
                'fields' => "id,tenant_id,company_name,unified_social_credit_code,company_registered_address,telephone_number"
            ]];
            $tenantAuthentications = teamones_request('teamones-im', 'tenant_authentication/select', $requestData)['data'];
            $tenantAuthenticationMap = array_column($tenantAuthentications['rows'], null, 'tenant_id');
        }
        foreach ($rows as &$row) {
            $mediaId = $row['attach_id'];
            $tenantId = $row['tenant_id'];
            $row['media'] = $mediaMap[$mediaId] ?? null;
            $row['tenant_authentication'] = $tenantAuthenticationMap[$tenantId] ?? null;
            $orders = [];
            foreach ($row['order_id'] as $orderId) {
                if (isset($orderMap[$orderId])) {
                    $orders['orders'][] = separate_complex_query_data($orderMap[$orderId], '__');
                }
            }
            $row = array_merge($row, $orders);
        }

        return [
            "total" => $total,
            'rows' => $rows,
        ];
    }

    /**
     * 创建发票申请
     * @param $data
     * @param int $userId
     * @param int $tenantId
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function createCustomerInvoiceApply($data, int $userId, int $tenantId)
    {
        $applyModel = model(CustomerInvoiceApplyModel::class);
        $orderModel = model(OrderModel::class);

        $orderIdList = explode(',', $data['order_ids']);

        $orderModule = Module::$moduleDictData["module_index_by_code"]['order'];
        $orderList = $orderModel
            ->join("settlement ON `order`.id = settlement.order_id")
            ->where([
                'order.id' => ['IN', implode(',', $orderIdList)],
                'order.from_tenant_id' => $tenantId,
                'order.party_a_invoice_status' => "no"
            ])
            ->field("`order`.*,settlement.money as settlement_money")
            ->select();

        if (0 >= count($orderList)) {
            throw new LogicException("order invoice status can`t apply invoice", ErrorCode::ORDER_INVOICE_STATUS_NOT_APPLY_INVOICE);
        }
        $data['money'] = 0;
        foreach ($orderList as $order) {
            $data['money'] += $order['settlement_money'];
        }


        $applyModel->startTrans();
        try {

            $applyData = [
                'link_ids' => $data['order_ids'],
                'module_id' => $orderModule['id'],
                'tenant_id' => $tenantId,
                'money' => $data['money'],
                'created_by' => $userId
            ];
            $res = $applyModel->addItem($applyData);
            $orderModel->modifyItem(['id' => ['in', $data['order_ids']], 'party_a_invoice_status' => 'submit']);

            $applyModel->commit();
            return $res;
        } catch (Throwable $e) {
            $applyModel->rollback();
            throw $e;
        }


    }

    /**
     * 上传发票给发票申请
     * @param $data
     * @return mixed
     * @throws Exception
     * @throws Throwable
     */
    public function uploadInvoiceCustomerInvoiceApply($data)
    {
        $mediaService = MediaService::getInstance();
        $mediaItem = $mediaService->generateMediaData($data['attached_media']);


        $mediaModel = model(MediaModel::class);

        $orderModel = model(OrderModel::class);
        $applyModel = model(CustomerInvoiceApplyModel::class);


        $apply = $applyModel->find($data['id']);


        if (empty($apply)) {
            throw new LogicException("missing customer invoice apply", ErrorCode::MISSING_CUSTOMER_INVOICE_APPLY);
        }

        $orders = $orderModel->where(['id' => ["IN", $apply['link_ids']]])->select();
        if (empty($orders)) {
            throw new LogicException("missing Order", ErrorCode::MISSING_ORDER);
        }
        $orderIdArray = array_column($orders, 'id', 'id');


        $orderInvoiceStatusField = "party_a_invoice_status";


        $orderModel->startTrans();
        try {
            $mediaData = $mediaModel->addItem($mediaItem);

            if (!$mediaData) {
                throw new LogicException("add media data failure" . $mediaModel->getError(), ErrorCode::MEDIA_ADD_FAILURE);
            }


            $res = $applyModel->modifyItem([
                'id' => $apply['id'],
                'attach_id' => $mediaData['id'],
                'status' => 'yes',
                'invoice_number' => $data['invoice_number'],
                'ship_number' => $data['ship_number'],
                'invoice_type' => $data['invoice_type'],
            ]);

            // 修改订单发票上传状态
            $orderModel->modifyItem([
                'id' => ['in', implode(',', $orderIdArray)],
                $orderInvoiceStatusField => "yes"
            ]);
            $orderModel->commit();
            $res['attached_media'] = $mediaData;
            return $res;
        } catch (Throwable $e) {
            $orderModel->rollback();
            throw $e;
        }
    }
}
